﻿/*************************************************************************
 *
 *    GNU Lesser General Public License Version 2.1
 *    =============================================
 *    Copyright 2005 by Sun Microsystems, Inc.
 *    901 San Antonio Road, Palo Alto, CA 94303, USA
 *
 *    This library is free software; you can redistribute it and/or
 *    modify it under the terms of the GNU Lesser General Public
 *    License version 2.1, as published by the Free Software Foundation.
 *
 *    This library is distributed in the hope that it will be useful,
 *    but WITHOUT ANY WARRANTY; without even the implied warranty of
 *    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 *    Lesser General Public License for more details.
 *
 *    You should have received a copy of the GNU Lesser General Public
 *    License along with this library; if not, write to the Free Software
 *    Foundation, Inc., 59 Temple Place, Suite 330, Boston,
 *    MA  02111-1307  USA
 *
 ************************************************************************/

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace CatchUpTv.Utilities
{
    /// <summary>
    /// A list that lazily fetches items and holds them in a cache.
    /// </summary>
    /// <typeparam name="T">The type of elements.</typeparam>
    public class LazyList<T> : IList<T>
    {
        /// <summary>
        /// The function used to retrieve items.
        /// </summary>
        Func<int, T> _itemRetriever;

        /// <summary>
        /// A cached copy of the items.
        /// </summary>
        T[] _itemCache;

        /// <summary>
        /// An array indicating if the item has been loaded into the item cache.
        /// </summary>
        bool[] _itemLoaded;

        /// <summary>
        /// The size of this list.
        /// </summary>
        int _size;

        public LazyList(Func<int, T> itemRetriever, int size)
        {
            _itemRetriever = itemRetriever;
            _itemCache = new T[size];
            _itemLoaded = new bool[size];
        }

        /// <summary>
        /// Gets or load the item at the given index.
        /// </summary>
        /// <param name="index">The index of the item to get.</param>
        /// <returns>The item.</returns>
        protected T GetOrLoadItem(int index)
        {
            if (!_itemLoaded[index])
            {
                _itemCache[index] = _itemRetriever(index);
                _itemLoaded[index] = true;
            }

            return _itemCache[index];
        }

        #region IList<T> Members

        public int IndexOf(T item)
        {
            for (int i = 0; i < _size; i++)
            {
                if (Object.Equals(item, GetOrLoadItem(i)))
                {
                    return i;
                }
            }

            return -1;
        }

        public void Insert(int index, T item)
        {
            throw new NotSupportedException("Lazy list can't be altered.");
        }

        public void RemoveAt(int index)
        {
            throw new NotSupportedException("Lazy list can't be altered.");
        }

        public T this[int index]
        {
            get
            {
                return GetOrLoadItem(index);
            }
            set
            {
                throw new NotSupportedException("Lazy list can't be altered.");
            }
        }

        #endregion

        #region ICollection<T> Members

        public void Add(T item)
        {
            throw new NotSupportedException("Lazy list can't be altered.");
        }

        public void Clear()
        {
            throw new NotSupportedException("Lazy list can't be altered.");
        }

        public bool Contains(T item)
        {
            return IndexOf(item) != -1;
        }

        public void CopyTo(T[] array, int arrayIndex)
        {
            for (int i = 0; i < _size; i++)
            {
                array[arrayIndex + i] = GetOrLoadItem(i);
            }
        }

        public int Count
        {
            get { return _size; }
        }

        public bool IsReadOnly
        {
            get { return true; }
        }

        public bool Remove(T item)
        {
            throw new NotSupportedException("Lazy list can't be altered.");
        }

        #endregion

        #region IEnumerable<T> Members

        public IEnumerator<T> GetEnumerator()
        {
            return new Enumerator<T>(this);
        }

        #endregion

        #region IEnumerable Members

        System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
        {
            return new Enumerator<T>(this);            
        }

        #endregion

        private class Enumerator<T> : IEnumerator<T>
        {
            LazyList<T> _list;

            int _currentIndex;

            public Enumerator(LazyList<T> list)
            {
                _list = list;
            }

            public T Current
            {
                get { return _list[_currentIndex]; }
            }

            object System.Collections.IEnumerator.Current
            {
                get { return _list[_currentIndex]; }
            }

            public bool MoveNext()
            {
                return ++_currentIndex < _list.Count;
            }

            public void Reset()
            {
                _currentIndex = 0;
            }

            public void Dispose()
            {
            }
        }
    }
}
